#pragma once

#include "trait.h"

namespace sfc::dyn {

template <class... I>
class Dyn {
  struct Self : I... {};
  struct Meta : I::Meta... {
    template <class X>
    Meta(trait::Type<X>) : I::Meta{trait::Type<X>{}}... {}
  };

  Self* _self;
  const Meta* _meta;

 public:
  Dyn() = default;

  template <class X>
  Dyn(X* p) : _self{reinterpret_cast<Self*>(p)} {
    static const auto meta = Meta{trait::Type<X>{}};
    _meta = &meta;
  }

  auto ptr() const -> const void* {
    return _self;
  }

  auto meta() const -> const Meta& {
    return *_meta;
  }

  auto operator->() -> Self* {
    return reinterpret_cast<Self*>(this);
  }

  auto operator->() const -> const Self* {
    return reinterpret_cast<const Self*>(this);
  }
};

template <class X>
using func_t = X;

template <class I>
auto self(I* this_ptr) -> I* {
  return reinterpret_cast<I**>(this_ptr)[0];
}

template <class I>
auto meta(I* this_ptr) -> const I::Meta& {
  using M = I::Meta;
  return reinterpret_cast<M**>(&this_ptr)[1][0];
}

template <class>
struct Fx;

template <class R, class... T>
struct Fx<R(T...)> {
  auto operator()(T&&... args) -> R {
    (self(this)->*meta(this).op_call)(static_cast<T&&>(args)...);
  }

  using Self = Fx;
  struct Meta {
    decltype(&Self::operator()) op_call;

    template <class X>
    Meta(trait::Type<X>) : op_call{(decltype(op_call))(&X::operator())} {}
  };
};

}  // namespace sfc::dyn

#define _SFC_DYN_FUNC(f) decltype(&Self::f) f;
#define _SFC_DYN_INIT(f) f(&X::f)
#define SFC_DYN_FUNC(f) (sfc::dyn::self(this)->*sfc::dyn::meta(this).f)
#define SFC_DYN_META(I, ...)                                                      \
  using Self = I;                                                                 \
  struct Meta {                                                                   \
    SFC_FOREACH(_SFC_DYN_FUNC, __VA_ARGS__)                                       \
    template <class X>                                                            \
    Meta(trait::Type<X>) : SFC_FOREACH_(_SFC_DYN_INIT, SFC_COMMA, __VA_ARGS__) {} \
  }


#define SFC_DYN_FN(name)
